package com.umeox.ota.mvvm.model;

import static com.umeox.ota.mvvm.viewModel.MainViewModel.SP_IS_COPY_FILE;

import android.annotation.SuppressLint;
import android.app.Application;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.IBinder;
import android.text.TextUtils;

import com.abupdate.iot_libs.OtaAgentPolicy;
import com.abupdate.iot_libs.data.constant.Error;
import com.abupdate.iot_libs.data.remote.VersionInfo;
import com.abupdate.iot_libs.engine.otaStatus.OtaStatus;
import com.abupdate.iot_libs.engine.otaStatus.OtaStatusMgr;
import com.abupdate.iot_libs.engine.security.FotaException;
import com.abupdate.iot_libs.interact.callback.ICheckVersionCallback;
import com.abupdate.iot_libs.interact.callback.IRebootUpgradeCallBack;
import com.abupdate.iot_libs.utils.SPFTool;
import com.abupdate.trace.Trace;
import com.umeox.ota.R;
import com.umeox.ota.data.constant.Constant;
import com.umeox.ota.data.constant.TRIGGER;
import com.umeox.ota.interact.callback.ICheckVersionInter;
import com.umeox.ota.interact.callback.IDownloadInter;
import com.umeox.ota.mvvm.BaseModel;
import com.umeox.ota.service.OtaService;
import com.umeox.ota.utils.FileUtils;
import com.umeox.ota.utils.RxTime;

import java.io.File;
import java.io.IOException;
import java.util.List;
import java.util.concurrent.TimeUnit;

import io.reactivex.Observable;
import io.reactivex.ObservableOnSubscribe;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.schedulers.Schedulers;

/**
 * @author fighter_lee
 * @date 2018/7/20
 */
public class MainModel extends BaseModel {

    private Context mCx;
    private static final String TAG = "MainModel";

    ICheckVersionInter iCheckVersionInter;

    OtaService.OtaBinder otaBinder;

    ServiceConnection connection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
            otaBinder = (OtaService.OtaBinder) iBinder;
        }

        @Override
        public void onServiceDisconnected(ComponentName componentName) {
            otaBinder = null;
        }
    };

    public MainModel(Application application) {
        super(application);
        this.mCx = application.getApplicationContext();
    }

    public OtaStatus getState() {
        OtaStatusMgr otaStatusMgr = mRepositoryManager.obtainDataManager(OtaStatusMgr.class);
        OtaStatus curStatus = otaStatusMgr.getCurStatus();
        return curStatus;
    }

    public void registerDownloadListener(IDownloadInter iDownloadInter) {
        OtaService.registerDownloadListener(iDownloadInter);
    }

    public void unRegisterDownloadListener() {
        OtaService.unRegisterDownloadListener();
    }

    public void registerUpgradeListener(IRebootUpgradeCallBack iRebootUpgradeCallBack) {
        OtaService.registerUpgradeListener(iRebootUpgradeCallBack);
    }

    public void unRegisterUpgradeListener() {
        OtaService.unRegisterUpgradeListener();
    }

    public void unRegisterCheckListener() {
        iCheckVersionInter = null;
    }

    /**
     * Rx+同步检测版本
     * 最短显示时间1.5s 超时时间30s
     */
    @SuppressLint("CheckResult")
    public void checkVersionTask(ICheckVersionInter onCheckVersionInter) {
        this.iCheckVersionInter = onCheckVersionInter;
        Observable<Long> lessTime = Observable.timer(1500, TimeUnit.MILLISECONDS);
        Observable<VersionInfo> checkObservable = Observable.create((ObservableOnSubscribe<VersionInfo>) e -> {
            if (prepare()) {
                otaBinder.checkVersion(TRIGGER.CHECK.CHECK_UI, new ICheckVersionCallback() {
                    @Override
                    public void onCheckSuccess(List<VersionInfo> list) {
                        if (list == null || list.size() > 1 || list.size() == 0) {
                            Trace.e(TAG, "checkVersionTask() 版本信息列表错误");
                            e.tryOnError(new FotaException(Error.ERROR));
                        } else {
                            e.onNext(list.get(0));
                        }
                        e.onComplete();
                    }

                    @Override
                    public void onCheckFail(int i) {
                        Trace.d(TAG, "onCheckFail: code:" + i + ",msg:" + Error.getErrorMessage(i));
                        e.tryOnError(new FotaException(i));
                        e.onComplete();
                    }
                });
            } else {
                Trace.e(TAG, "checkVersionTask() service not bind");
                e.tryOnError(new Exception("service not bind"));
                e.onComplete();
            }
        }).subscribeOn(Schedulers.io());
        Observable.zip(lessTime, checkObservable, (aLong, versionInfo) -> versionInfo).timeout(30, TimeUnit.SECONDS)
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(
                        versionInfo -> {
                            if (iCheckVersionInter != null) {
                                iCheckVersionInter.onCheckSuccess(versionInfo);
                            }
                        },
                        throwable -> {
                            if (iCheckVersionInter == null) {
                                return;
                            }
                            if (throwable instanceof FotaException) {
                                int reasonCode = ((FotaException) throwable).getReasonCode();
                                if (reasonCode == Error.NETWORK_ERROR) {
                                    iCheckVersionInter.onFailed(mCx.getString(R.string.net_error_tips));
                                    return;
                                }
                            }
                            iCheckVersionInter.onFailed(mCx.getString(R.string.update_last_version));
                        });
    }

    /**
     * 下载
     * FOTA必选功能
     */
    @SuppressLint("CheckResult")
    public void download(final IDownloadInter iDownloadInter) {
        Observable.create((ObservableOnSubscribe<Boolean>) e -> {
            e.onNext(prepare());
            e.onComplete();
        })
                .subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .doOnNext(aBoolean -> {
                    if (!aBoolean) {
                        Trace.e(TAG, "download() service not bind");
                        iDownloadInter.onFailed(Constant.BIND_SERVER_ERROR);
                    }
                })
                .observeOn(Schedulers.io())
                .subscribe(aBoolean -> {
                    if (aBoolean) {
                        otaBinder.download(TRIGGER.DOWNLOAD.DOWNLOAD_UI, iDownloadInter);
                    }
                });


    }

    /**
     * 升级
     * FOTA必选功能
     */
    @SuppressLint("CheckResult")
    public void rebootUpgrade(final IRebootUpgradeCallBack iRebootUpgradeCallBack) {
        Observable.create((ObservableOnSubscribe<Boolean>) e -> {
            e.onNext(prepare());
            e.onComplete();
        })
                .subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .doOnNext(aBoolean -> {
                    if (!aBoolean) {
                        iRebootUpgradeCallBack.onError(Constant.BIND_SERVER_ERROR);
                    }
                })
                .observeOn(Schedulers.io())
                .subscribe(aBoolean -> {
                    if (aBoolean) {
                        otaBinder.upgrade(TRIGGER.UPGRADE.UPGRADE_UI, iRebootUpgradeCallBack);
                    }
                }, throwable -> Trace.d(TAG, "rebootUpgrade onError() " + throwable.getMessage()));

    }

    /**
     * 下载取消
     */
    public boolean downloadCancel() {
        //prepare()是耗时任务，请在子线程中执行取消任务
        if (getState() == OtaStatus.DOWNLOADING) {
            if (prepare()) {
                return otaBinder.downloadCancel();
            }
        }
        return false;
    }

    /**
     * 本地升级
     *
     * @param localFilePath
     * @param iRebootUpgradeCallBack
     */
    @SuppressLint("CheckResult")
    public void localRebootUpgrade(String localFilePath, IRebootUpgradeCallBack iRebootUpgradeCallBack) {
        Observable.create((ObservableOnSubscribe<Boolean>) e -> {
            e.onNext(prepare());
            e.onComplete();
        })
                .subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .doOnNext(aBoolean -> {
                    if (!aBoolean) {
                        iRebootUpgradeCallBack.onError(Constant.BIND_SERVER_ERROR);
                    }
                })
                .observeOn(Schedulers.io())
                .subscribe(aBoolean -> {
                    File file = null;
                    try {
                        file = new File(new File(OtaAgentPolicy.getOtaEntityController().getMainEntity().getFilePath())
                                .getParentFile()
                                .getAbsolutePath() + File.separator + "local" + File.separator + "update.zip");
                    } catch (FotaException e) {
                        SPFTool.putBoolean(SP_IS_COPY_FILE, false);
                        e.printStackTrace();
                    }
                    boolean copy;
                    if (file.exists() && TextUtils.equals(FileUtils.getFileMD5ToString(file), FileUtils.getFileMD5ToString(localFilePath))) {
                        copy = true;
                    } else {
                        if (file.exists()) {
                            file.delete();
                        }
                        copy = FileUtils.copyFile(new File(localFilePath), file);
                        Trace.d(TAG, "run: copy file " + (copy ? "success" : "failed"));
                    }
                    if (!copy) {
                        SPFTool.putBoolean(SP_IS_COPY_FILE, false);
                        throw new IOException("copy file failed");
                    }
                    SPFTool.putBoolean(SP_IS_COPY_FILE, false);
                    if (aBoolean) {
                        otaBinder.localUpgrade(file.getAbsolutePath(), TRIGGER.UPGRADE.UPGRADE_UI, iRebootUpgradeCallBack);
                    }
                }, throwable -> Trace.d(TAG, "rebootUpgrade onError() " + throwable.getMessage()));
    }

    /**
     * 是否和OTAService建立连接
     *
     * @return
     */
    private boolean prepare() {
        if (otaBinder == null) {
            Intent intent = new Intent(mCx, OtaService.class);
            mCx.bindService(intent, connection, Context.BIND_AUTO_CREATE);
            RxTime.newInstance().setTimeout(20 * 1000)
                    .setPeriod(2 * 100)
                    .setICondition(() ->
                            otaBinder != null
                    ).execute();
            return otaBinder != null;
        }
        return true;
    }
}
